﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Loki.Data;
using Loki.Engine;
using Loki.Utils;

namespace Luna.Data.Test
{
    [TestFixture("SqlServer")]
    [TestFixture("SqlCe")]
    public abstract class BaseTest<TService, TEntity, TKey> : AssertionHelper
        where TService : class, IDataProvider
        where TEntity : class, IDataEntity<Guid>
    {
        protected const string TEST_PREFIX = "#Test";

        protected static readonly DateTime BASE_DATE = new DateTime(2100, 1, 1);

        protected static Guid BUDGET = new Guid("{E1A89D47-F8EB-455B-86FB-879E90963A01}");

        protected static Guid BUDGET_CATEGORY = new Guid("{E1A89D47-F8EB-455B-86FB-879E90963A01}");

        private const string STRING_FORMAT = "{0}_{1}_{2}";

        public BaseTest(string contextType)
        {
            Randomizer = new Random();
        }

        public abstract Action<TKey> Delete { get; }

        public abstract Predicate<TEntity> DeletePredicate { get; }

        public abstract Func<TKey, TEntity> Get { get; }

        public abstract Func<IEnumerable<TEntity>> GetAll { get; }

        public abstract Func<TEntity, TKey> KeyGetter { get; }

        public abstract Action<TEntity> Save { get; }

        public TService Service
        {
            get
            {
                return Context.Get<TService>();
            }
        }

        protected IDataContext Context
        {
            get
            {
                return ToolKit.Engine.GetDataModel<IDataContext>();
            }
        }

        protected string ContextName { get; private set; }

        protected Random Randomizer
        {
            get;
            private set;
        }

        public abstract bool AreSame(TEntity P_Entity1, TEntity P_Entity2);

        [TearDown]
        public virtual void Clean()
        {
            foreach (var L_Item in GetAll().Where(x => DeletePredicate(x)))
            {
                Delete(KeyGetter(L_Item));
            }
        }

        public virtual TEntity Create()
        {
            return Context.Create<TEntity>();
        }

        public virtual void CreateTestData()
        {
        }

        [Test]
        public void TestInsertAndDelete()
        {
            int L_Countb = GetAll().Count();
            TEntity L_Entity1 = Create();
            TEntity L_Entity2 = Create();
            Save(L_Entity1);
            Save(L_Entity2);
            Delete(KeyGetter(L_Entity1));

            int L_Counta = GetAll().Count();
            Expect(L_Counta - L_Countb, Is.EqualTo(1));

            TEntity L_Read = Get(KeyGetter(L_Entity1));
            Expect(L_Read, Is.Null);
        }

        [Test(Description = "Insert and get")]
        public void TestInsertAndGet()
        {
            TEntity L_Entity = Create();
            Save(L_Entity);
            TEntity L_ReadEntity = Get(KeyGetter(L_Entity));
            Expect(AreSame(L_Entity, L_ReadEntity), Is.True);
        }

        [Test]
        public void TestInsertAndGetAll()
        {
            int L_Countb = GetAll().Count();
            TEntity L_Entity1 = Create();
            TEntity L_Entity2 = Create();
            TEntity L_Entity_Modified = Create();
            L_Entity_Modified.PK = L_Entity1.PK;
            L_Entity_Modified.AlreadyPersisted = true;
            Save(L_Entity1);
            Save(L_Entity2);
            Save(L_Entity_Modified);

            int L_Counta = GetAll().Count();
            Expect(L_Counta - L_Countb, Is.EqualTo(2));
        }

        [Test]
        public void TestUpdateAndGet()
        {
            TEntity L_Entity = Create();
            TEntity L_Entity_Modified = Create();
            L_Entity_Modified.PK = L_Entity.PK;
            Save(L_Entity_Modified);

            TEntity L_Read = Get(KeyGetter(L_Entity));
            Expect(AreSame(L_Entity_Modified, L_Read), Is.True);
        }

        protected bool GetBoolean()
        {
            return Randomizer.Next(10) % 2 == 0;
        }

        protected DateTime GetDate()
        {
            return BASE_DATE.AddDays(Randomizer.Next(1000));
        }

        protected decimal GetDecimal()
        {
            return Convert.ToDecimal(Randomizer.Next(100000));
        }

        protected int GetInteger()
        {
            return Randomizer.Next();
        }

        protected T GetNewEnum<T>()
        {
            var L_Values = Enum.GetValues(typeof(T));
            return (T)L_Values.GetValue(Randomizer.Next(L_Values.Length - 1));
        }

        protected string GetString(string P_Name)
        {
            return string.Format(CultureInfo.InvariantCulture, STRING_FORMAT, TEST_PREFIX, Randomizer.Next(1000), P_Name);
        }

        protected string GetString(string P_Name, int P_CharCount)
        {
            var L_BaseString = GetString(P_Name);
            return L_BaseString.Substring(0, Math.Min(P_CharCount, L_BaseString.Length));
        }
    }
}
